package com.mib.browser;

import com.ireasoning.protocol.Listener;
import com.ireasoning.protocol.Msg;
import com.ireasoning.protocol.TimeoutException;
import com.ireasoning.protocol.snmp.*;
import com.mib.snmp.Snmp4jUtil;
import com.mib.snmp.SnmpBuilder;
import com.mib.snmp.SnmpWalkThread;
import org.snmp4j.security.*;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;

import javax.swing.*;
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;

public class SNMPManager implements Listener {

    private SnmpWalkThread snmpWalkThread;
    private int port = 161;
    private int version = 1;
    private String host = "localhost";
    private SecurityLevel securityLevel;
    private OID authProtocol;
    private String authPassword;
    private OID privProtocol;
    private String privPassword;
    private String readCommunity = "public";
    private String writeCommunity = "public";
    private String user;
    private String[] oids;
    private String oid;
    private boolean isSnmpV3;
    private boolean numericalOID = false;
    private String[] mibFiles;
    private String mibString;
    private OperationTableModel operationTableModel;
    private JComboBox oidComboBox;
    private JButton goButton;
    private JFrame frame;

    public SNMPManager(JFrame frame, JButton goButton, JComboBox oidComboBox, OperationTableModel model) {
        this.frame = frame;
        this.goButton = goButton;
        this.oidComboBox = oidComboBox;
        this.operationTableModel = model;
    }

    public void snmpGet() {
        try {
            SnmpBuilder build = new SnmpBuilder()
                    .setIp(this.getHost())
                    .setPort(this.getPort())
                    .setVersion(this.getVersion())
                    .setCommunity(this.getReadCommunity())
                    .setSecurityName(this.getUser())
                    .setAuthGeneric(this.getAuthProtocol())
                    .setAuthPass(this.getAuthPassword())
                    .setPrivacyGeneric(this.getPrivProtocol())
                    .setPrivacyPass(this.getPrivPassword())
                    .setSecurityLevel(this.getSecurityLevel())
                    .build();
            List<VariableBinding> list = Snmp4jUtil.snmpGet(build.getSnmp(), build.getTarget(), this.getOid());

            for(VariableBinding vb : list){
                if(vb != null){
                    OperationResult r = new OperationResult(vb.getOid().toString(), vb.getVariable().toString());
                    this.operationTableModel.addOperationResult(r);
                }
            }
        } catch (Exception e){
            JOptionPane.showMessageDialog(this.frame, e.getMessage(), "警告", JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
        }
    }

    public String snmpGetNext() {
        String lastOid = "";
        try {
            SnmpBuilder build = new SnmpBuilder()
                    .setIp(this.getHost())
                    .setPort(this.getPort())
                    .setVersion(this.getVersion())
                    .setCommunity(this.getReadCommunity())
                    .setSecurityName(this.getUser())
                    .setAuthGeneric(this.getAuthProtocol())
                    .setAuthPass(this.getAuthPassword())
                    .setPrivacyGeneric(this.getPrivProtocol())
                    .setPrivacyPass(this.getPrivPassword())
                    .setSecurityLevel(this.getSecurityLevel())
                    .build();
            List<VariableBinding> list = Snmp4jUtil.snmpGetNext(build.getSnmp(), build.getTarget(), this.getOid());

            for(VariableBinding vb : list){
                if(vb != null){
                    OperationResult r = new OperationResult(vb.getOid().toString(), vb.getVariable().toString());
                    this.operationTableModel.addOperationResult(r);
                    lastOid = r.getOid();
                }
            }
        } catch (Exception e){
            JOptionPane.showMessageDialog(this.frame, e.getMessage(), "警告", JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
        }
        return lastOid;
    }

    public void snmpWalk() {
        String lastOid = this.getOid();
        SnmpBuilder build = new SnmpBuilder()
                .setIp(this.getHost())
                .setPort(this.getPort())
                .setVersion(this.getVersion())
                .setCommunity(this.getReadCommunity())
                .setSecurityName(this.getUser())
                .setAuthGeneric(this.getAuthProtocol())
                .setAuthPass(this.getAuthPassword())
                .setPrivacyGeneric(this.getPrivProtocol())
                .setPrivacyPass(this.getPrivPassword())
                .setSecurityLevel(this.getSecurityLevel())
                .build();
        if(this.snmpWalkThread != null && this.snmpWalkThread.isAlive()){
            this.snmpWalkThread.stop();
        }
        this.snmpWalkThread = new SnmpWalkThread(lastOid, build, this.goButton, this.oidComboBox, this.operationTableModel);
        this.snmpWalkThread.start();
    }

    public void snmpSet(SnmpVarBind vb) {
        SnmpVarBindList vblist = new SnmpVarBindList();
        vblist.add(vb);

        try {
            SnmpSession session = new SnmpSession(this.getHost(), this.getPort(), this.getReadCommunity(), this.getWriteCommunity(), this.getVersion());
            if (this.isIsSnmpV3()) {
                session.setV3Params(this.getUser(), this.getAuthProtocol().toString(), this.getAuthPassword(), this.getPrivProtocol().getSyntax(), this.getPrivPassword());
            }

            SnmpPdu pdu = new SnmpPdu(-93);
            pdu.addVarBinds(vblist);
            SnmpPdu retPdu = session.snmpSetRequest(pdu);
            SnmpVarBind[] vars = retPdu.getVarBinds();

            for(int i = 0; i < vars.length; ++i) {
                OperationResult r = this.getOperationResult(vars[i]);
                if (r != null) {
                    this.operationTableModel.addOperationResult(r);
                }
            }

            session.close();
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this.frame, e.getMessage(), "警告", JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
        }

    }

    public void handleMsg(Object session, Msg msg) {
        SnmpPdu pdu = (SnmpPdu)msg;
        if (pdu.getErrorStatus() != 0) {
            System.out.println("Error: " + SnmpErrorStatus.ERR_STRINGS[pdu.getErrorStatus()]);
        } else if (pdu.isSnmpV3AuthenticationFailed()) {
            System.out.println("Authentication failed");
        } else if (!pdu.hasMore()) {
            System.out.println("<End of MIB View Reached>");
            ((SnmpSession)session).close();
        } else {
            this.print(pdu);
        }
    }

    private OperationResult getOperationResult(SnmpVarBind var) {
        if (var == null) {
            return null;
        } else {
            SnmpDataType n = var.getName();
            SnmpDataType v = var.getValue();
            if (n == null) {
                System.out.println("SnmpVarBind 's name is null");
            }

            String oid = n.toString();
            String value = v.toString();
            if (MibUtil.isMibFileLoaded()) {
                NameValue nv = MibUtil.translate(oid, value, true);
                if (nv != null) {
                    if (!this.isNumericalOID()) {
                        oid = nv.getName();
                    }

                    value = nv.getValue();
                }
            }

            if (v.getType() == 67) {
                value = ((SnmpTimeTicks)v).getTimeString();
            } else if (v.getType() == 130) {
                System.out.println("End of MIB reached.");
            } else if (v.getType() == 128) {
                System.out.println("No such object.");
            } else if (v.getType() == 129) {
                System.out.println("No such Instance.");
            }

            return new OperationResult(oid, value);
        }
    }

    protected void print(SnmpPdu pdu) {
        this.print(pdu, System.out);
    }

    protected void print(SnmpPdu pdu, PrintStream p) {
        if (pdu.getErrorStatus() > 0) {
            System.out.println("PDU error status = " + pdu.getErrorStatusString());
        } else if (pdu.isSnmpV3AuthenticationFailed()) {
            System.out.println("Authentication failure");
            this.printAuthFailReason(pdu);
        } else {
            this.print(pdu.getVarBinds(), p);
        }
    }

    private void printAuthFailReason(SnmpPdu pdu) {
        SnmpVarBind vb = pdu.getFirstVarBind();
        if (vb != null) {
            SnmpOID oid = vb.getName();
            if (oid.equals(AgentUsmStats.USM_STATS_UNKNOWN_USER_NAMES)) {
                System.out.println("Reason: Unknown user name");
            } else if (oid.equals(AgentUsmStats.USM_STATS_DECRYPTION_ERRORS)) {
                System.out.println("USM decryption error");
            } else if (oid.equals(AgentUsmStats.USM_STATS_WRONG_DIGESTS)) {
                System.out.println("Wrong digest");
            } else if (oid.equals(AgentUsmStats.USM_STATS_UNKNOWN_ENGINE_IDS)) {
                System.out.println("Unkown engine ID");
            } else if (oid.equals(AgentUsmStats.USM_STATS_NOT_IN_TIMEWINDOWS)) {
                System.out.println("Not in time windows");
            } else if (oid.equals(AgentUsmStats.USM_STATS_UNSUPPORTED_SEC_LEVELS)) {
                System.out.println("Unsupported security levels");
            }
        }

    }

    protected void print(SnmpVarBind[] varbinds) {
        this.print(varbinds, System.out);
    }

    protected void print(SnmpVarBind[] varbinds, PrintStream p) {
        for(int i = 0; i < varbinds.length; ++i) {
            this.print(varbinds[i], p);
        }

    }

    protected void print(SnmpVarBind var) {
        this.print(var, System.out);
    }

    protected void print(SnmpVarBind var, PrintStream p) {
        if (var != null) {
            SnmpDataType n = var.getName();
            SnmpDataType v = var.getValue();
            if (n == null) {
                p.println("SnmpVarBind 's name is null");
            }

            String oid = n.toString();
            String value = v.toString();
            if (MibUtil.isMibFileLoaded()) {
                NameValue nv = MibUtil.translate(oid, value, true);
                if (nv != null) {
                    if (!this.isNumericalOID()) {
                        oid = nv.getName();
                    }

                    value = nv.getValue();
                }
            }

            if (v.getType() == 67) {
                value = ((SnmpTimeTicks)v).getTimeString();
            } else {
                if (v.getType() == 130) {
                    p.println("End of MIB reached.");
                    return;
                }

                if (v.getType() == 128) {
                    p.println("没有此对象值");
                    return;
                }

                if (v.getType() == 129) {
                    p.println("No such Instance.");
                    return;
                }
            }

            p.println(oid + "\r\nValue (" + v.getTypeString() + "): " + value + "\r\n");
        }
    }

    public void stopSnmpWalk() {
        if(this.snmpWalkThread != null && this.snmpWalkThread.isAlive()){
            this.snmpWalkThread.stop();
        }
    }

    public String getOid() {
        return this.oid;
    }

    public void setOid(String oid) {
        this.oid = oid;
    }

    public int getPort() {
        return this.port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public int getVersion() {
        return this.version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public String getHost() {
        return this.host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public SecurityLevel getSecurityLevel() {
        return securityLevel;
    }

    public SNMPManager setSecurityLevel(SecurityLevel securityLevel) {
        this.securityLevel = securityLevel;
        return this;
    }

    public OID getAuthProtocol() {
        return this.authProtocol;
    }

    public void setAuthProtocol(OID authProtocol) {
        this.authProtocol = authProtocol;
    }

    public String getAuthPassword() {
        return this.authPassword;
    }

    public void setAuthPassword(String authPassword) {
        this.authPassword = authPassword;
    }

    public OID getPrivProtocol() {
        return this.privProtocol;
    }

    public void setPrivProtocol(OID privProtocol) {
        this.privProtocol = privProtocol;
    }

    public String getPrivPassword() {
        return this.privPassword;
    }

    public void setPrivPassword(String privPassword) {
        this.privPassword = privPassword;
    }

    public String getReadCommunity() {
        return this.readCommunity;
    }

    public void setReadCommunity(String readCommunity) {
        this.readCommunity = readCommunity;
    }

    public String getWriteCommunity() {
        return this.writeCommunity;
    }

    public void setWriteCommunity(String writeCommunity) {
        this.writeCommunity = writeCommunity;
    }

    public String getUser() {
        return this.user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String[] getOids() {
        return this.oids;
    }

    public void setOids(String[] oids) {
        this.oids = oids;
    }

    public boolean isIsSnmpV3() {
        return this.isSnmpV3;
    }

    public void setIsSnmpV3(boolean isSnmpV3) {
        this.isSnmpV3 = isSnmpV3;
    }

    public boolean isNumericalOID() {
        return this.numericalOID;
    }

    public void setNumericalOID(boolean numericalOID) {
        this.numericalOID = numericalOID;
    }

    public String[] getMibFiles() {
        return this.mibFiles;
    }

    public void setMibFiles(String[] mibFiles) {
        this.mibFiles = mibFiles;
    }

    public String getMibString() {
        return this.mibString;
    }

    public void setMibString(String mibString) {
        this.mibString = mibString;
    }
}
